package com.destroyer.system.wechat.service.impl;


import com.destroyer.common.entity.system.ResultEnum;
import com.destroyer.common.exception.ServiceException;
import com.destroyer.common.util.Func;
import com.destroyer.core.entity.wechat.bo.WechatPrepayBO;
import com.destroyer.core.entity.wechat.vo.WechatPrepayWithRequestPaymentVO;
import com.destroyer.core.entity.wechat.vo.WechatTransactionVO;
import com.destroyer.core.service.wechat.IWechatFeignService;
import com.destroyer.system.wechat.config.WechatConfig;
import com.destroyer.system.wechat.config.WechatPaymentsConfig;
import com.destroyer.system.wechat.service.IWechatPaymentsService;
import com.wechat.pay.java.core.Config;

import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.NonceUtil;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.time.Instant;

/**
 * 标题：WechatPaymentsServiceImpl
 * 说明：微信支付服务接口服务实现
 * 时间：2024/3/12
 * 作者：admin
 */
@Service
@Slf4j
public class WechatPaymentsServiceImpl implements IWechatPaymentsService {

    @Autowired
    private WechatConfig weChatConfig;

    @Autowired
    private IWechatFeignService wechatFeignService;


    @Autowired
    private WechatPaymentsConfig wechatPaymentsConfig;


    /**
     * 微信建立支付预订单
     *
     * @param req 支付预订单ID
     * @return
     */
    @Override
    public String prepay(WechatPrepayBO req) {
        String rsp = null;
        //入参效验
        if (null == req) {
            throw new ServiceException(ResultEnum.PARAM_MISS, "入参不能为空！");
        } else {
            if (Func.isEmpty(req.getOrderNo())) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，订单编号不能为空！");
            }
            if (Func.isEmpty(req.getTotal())) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，付款金额不能为空！");
            }
            if (req.getTotal() <= 0) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，付款金额不能小于等于0！");
            }
        }
        // 构建service
        JsapiService wechatJsapiService = wechatPaymentsConfig.getWechatJsapiService();
        PrepayRequest request = this.getPrepayRequestByPrepayBO(req);
        // 调用下单方法，得到应答
        try {
            PrepayResponse prepayResponse = wechatJsapiService.prepay(request);
            rsp = prepayResponse.getPrepayId();
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            throw new ServiceException(e.getMessage());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            throw new ServiceException(e.getMessage());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            throw new ServiceException(e.getMessage());
        }
        if (Func.isEmpty(rsp)) {
            throw new ServiceException(ResultEnum.FAILURE, "建立微信预支付订单失败！");
        }
        return rsp;

    }


    /**
     * 下单并生成调起支付的参数
     *
     * @param req
     * @return
     */
    @Override
    public WechatPrepayWithRequestPaymentVO prepayWithRequestPayment(WechatPrepayBO req) {
        //入参效验
        if (null == req) {
            throw new ServiceException(ResultEnum.PARAM_MISS, "入参不能为空！");
        } else {
            if (Func.isEmpty(req.getOrderNo())) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，订单编号不能为空！");
            }
            if (Func.isEmpty(req.getTotal())) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，付款金额不能为空！");
            }
            if (req.getTotal() <= 0) {
                throw new ServiceException(ResultEnum.PARAM_MISS, "建立微信支付预订单，付款金额不能小于等于0！");
            }
        }
        WechatPrepayWithRequestPaymentVO rsp = new WechatPrepayWithRequestPaymentVO();
        PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = new PrepayWithRequestPaymentResponse();
        JsapiServiceExtension wechatJsapiServiceExtension = wechatPaymentsConfig.getWechatJsapiServiceExtension();
        try {
            PrepayRequest request = this.getPrepayRequestByPrepayBO(req);
            prepayWithRequestPaymentResponse = wechatJsapiServiceExtension.prepayWithRequestPayment(request);
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            throw new ServiceException(e.getMessage());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            throw new ServiceException(e.getMessage());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            throw new ServiceException(e.getMessage());
        }
        BeanUtils.copyProperties(prepayWithRequestPaymentResponse,rsp);
        return rsp;
    }


    /**
     * 通过预支付ID生成调起支付的参数
     *
     * @param req
     * @return
     */
    @Override
    public WechatPrepayWithRequestPaymentVO prepayWithRequestPaymentByPrepayId(String req) {
        WechatPrepayWithRequestPaymentVO rsp = new WechatPrepayWithRequestPaymentVO();
        Config config = wechatPaymentsConfig.getWechatRsaAutoCertificateConfig();
        String prepayId = req;
        long timestamp = Instant.now().getEpochSecond();
        String nonceStr = NonceUtil.createNonce(32);
        String packageVal = "prepay_id=" + prepayId;
        String appId = weChatConfig.getAppid();
        String message =
                appId + "\n" + timestamp + "\n" + nonceStr + "\n" + packageVal + "\n";
        log.info("Message for RequestPayment signatures is[{}]", message);
        String sign = config.createSigner().sign(message).getSign();
        rsp.setAppId(appId);
        rsp.setTimestamp(String.valueOf(timestamp));
        rsp.setNonceStr(nonceStr);
        rsp.setPackageVal(packageVal);
        rsp.setSignType("RSA");
        rsp.setPaySign(sign);
        return rsp;
    }


    /**
     * 支付回调
     * @param requestBody
     * @return
     */
    @Override
    public ResponseEntity.BodyBuilder paymentNotify(String requestBody) {
        String nonceStr = NonceUtil.createNonce(32);
        long timestamp = Instant.now().getEpochSecond();
        NotificationConfig config = (NotificationConfig) wechatPaymentsConfig.getWechatRsaAutoCertificateConfig();
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(weChatConfig.getMerchantSerialNumber())
                .nonce(nonceStr)
                .signature("RSA")
                .timestamp(String.valueOf(timestamp))
                .body(requestBody)
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            //回调业务接口
            WechatTransactionVO wechatTransactionVO = new WechatTransactionVO();
            BeanUtils.copyProperties(transaction, wechatTransactionVO);
            if(Func.isNotEmpty(transaction.getAmount())){
                wechatTransactionVO.setTotal(transaction.getAmount().getTotal());
            }
            wechatFeignService.wechatPaymentSuccessNotify(wechatTransactionVO);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.info("sign verification failed", e);
           throw new ServiceException(e.getMessage());
        }
        ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);

        return bodyBuilder;
    }

    @Override
    public boolean closeOrderByOutTradeNo(String req) {
        boolean rsp = false;
        if(Func.isEmpty(req)){
            throw new ServiceException("关闭微信支付订单，入参业务订单编号不能为空！");
        }

        // 调用接口
        JsapiServiceExtension wechatJsapiServiceExtension = wechatPaymentsConfig.getWechatJsapiServiceExtension();
        try {
            CloseOrderRequest request = new CloseOrderRequest();
            // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
            request.setOutTradeNo(req);
            request.setMchid(weChatConfig.getMchid());
            wechatJsapiServiceExtension.closeOrder(request);
            rsp = true;
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            throw new ServiceException(e.getMessage());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            throw new ServiceException(e.getMessage());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            throw new ServiceException(e.getMessage());
        }
        return rsp;
    }


    /**
     * 通过微信支付下单业务对象构造微信预下单请求参数
     *
     * @param req
     * @return
     */
    private PrepayRequest getPrepayRequestByPrepayBO(WechatPrepayBO req) {
        PrepayRequest rsp = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(req.getTotal());
        rsp.setAmount(amount);
        rsp.setAppid(weChatConfig.getAppid());
        rsp.setMchid(weChatConfig.getMchid());
        rsp.setDescription(req.getDescription());
        rsp.setNotifyUrl(weChatConfig.getPrepayNotifyUrl());
        rsp.setOutTradeNo(req.getOrderNo());
        return rsp;
    }



}
